#pragma once

#include <iostream>
#include <queue>
#include <pthread.h>
#include "Task.hpp"
#include "Log.hpp"

#define NUM 6

class ThreadPool
{
private:
    int num;
    std::queue<Task> task_queue;
    pthread_mutex_t lock;
    pthread_cond_t cond;

    ThreadPool(int _num = NUM) : num(_num)
    {
        pthread_mutex_init(&lock, nullptr);
        pthread_cond_init(&cond, nullptr);
    }

    ThreadPool(const ThreadPool &) = delete;

    static ThreadPool *single_instance;

public:
    static ThreadPool *getinstance()
    {
        static pthread_mutex_t _mutex = PTHREAD_MUTEX_INITIALIZER;
        if (single_instance == nullptr)
        {
            pthread_mutex_lock(&_mutex);
            if (single_instance == nullptr)
            {
                single_instance = new ThreadPool();
                single_instance->InitThreadPool();
            }
            pthread_mutex_unlock(&_mutex);
        }
    }

    bool TaskQueueIsEmpty() { return !task_queue.size(); }
    void Lock() { pthread_mutex_lock(&lock); }
    void Unlock() { pthread_mutex_unlock(&lock); }
    void ThreadWait() { pthread_cond_wait(&cond, &lock); }
    void ThreadWakeup() { pthread_cond_signal(&cond); }

    void PushTask(const Task &task) // in
    {
        Lock();
        task_queue.push(task);
        Unlock();
        ThreadWakeup();
    }

    void PopTask(Task &task) // out
    {
        task = task_queue.front();
        task_queue.pop();
    }

    static void *ThreadRoutine(void *args)
    {
        ThreadPool *tp = static_cast<ThreadPool *>(args);
        while (1)
        {
            Task t;
            tp->Lock();
            while (tp->TaskQueueIsEmpty())
            {
                tp->ThreadWakeup();
            }
            tp->PopTask(t);
            tp->Unlock();
            t.ProcessOn();
        }
    }
    bool InitThreadPool()
    {
        for (int i = 0; i < num; i++)
        {
            pthread_t tid;
            int n = pthread_create(&tid, nullptr, ThreadRoutine, this);
            if (n != 0)
            {
                LOG(FATAL, "create thread pool error!");
                return 0;
            }
        }
        LOG(INFO, "create thread pool success!");
        return 1;
    }
    ~ThreadPool()
    {
        pthread_mutex_destroy(&lock);
        pthread_cond_destroy(&cond);
    }
};

ThreadPool *ThreadPool::single_instance = nullptr;